﻿using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent;
using TrackSystem.Contracts;
using TrackSystem.Entities.Extensions;
using TrackSystem.IO.Formula;
using TrackSystem.IO.Utils;

namespace TrackSystem.IO.Alarm
{
    public class AlarmFactory
    {
        #region 成员变量
        /// <summary>
        /// 是否载入
        /// </summary>
        public bool IsLoad { get; protected set; }
        /// <summary>
        /// 线程运行状态
        /// </summary>
        public bool Running { get; protected set; }
        /// <summary>
        /// 线程运行周期设定
        /// </summary>
        public int Interval { get; set; }
        /// <summary>
        /// 线程实际运行周期
        /// </summary>
        public int Cycle { get; protected set; }
        /// <summary>
        /// 线程耗时实际长度
        /// </summary>
        public int Lapse { get; protected set; }
        
        #endregion
        public static AlarmFactory Instance { get; private set; }
        /// <summary>
        /// 报警信息缓冲区
        /// </summary>
        private ConcurrentDictionary<Guid, AlarmItem>? _items;
        
        /// <summary>
        /// 已经确认的报警信息
        /// </summary>
        private ConcurrentDictionary<Guid, AlarmItem>? _confirmItems;

        /// <summary>
        /// 报警数据库操作
        /// </summary>
        private static IServiceScopeFactory? _serviceScopeFactory;
        
        private Thread _thread;

        /// <summary>
        /// 构造函数
        /// </summary>
        static AlarmFactory()
        {
            Instance = new AlarmFactory();
            Instance.Initialize();
        }

        /// <summary>
        /// 初始化函数
        /// </summary>
        private void Initialize()
        {
            _items = [];
            _confirmItems = [];
            Interval = 100;  // 循环时间100ms
        }

        /// <summary>
        /// 载入之前的报警信息
        /// </summary>
        /// <param name="wrapper"></param>
        /// <param name="serviceScope"></param>
        /// <returns></returns>
        public async Task<ResultMsg> Load(IRepositoryWrapper wrapper, IServiceScopeFactory serviceScope)
        {
            try
            {
                // 载入之前没有确认过的所有报警
                var alarmItems = await wrapper.Alarm.GetAll().Where(a => !a.Confirm).ToArrayAsync();
                foreach (var alarmItem in alarmItems)
                {
                    var item = AlarmItem.CreateAlarmItem(alarmItem);
                    if (_items is not null && !_items.ContainsKey(item.Identify))
                    {
                        _items.TryAdd(item.Identify, item);
                    }
                }

                _serviceScopeFactory = serviceScope;

                // 开启线程
                _thread = new Thread(WorkerThread)
                {
                    IsBackground = true
                };
                Running = true;
                _thread.Start();
                
                return ResultMsg.Succ("载入报警信息成功");
            }
            catch (Exception ex)
            {
                Insert($"载入报警变发生错误, {ex.Message}", AlarmCategory.ALARM, "Alarm");
            }

            return ResultMsg.Fail("载入报警信息失败");
        }

        /// <summary>
        /// 清理缓冲区
        /// </summary>
        public void Dispose()
        {
            Running = false;
            _thread?.Join();
            
            SaveToTable(true, true);
            _items?.Clear();
            _confirmItems?.Clear();
        }

        /// <summary>
        /// 插入一条报警信息
        /// </summary>
        /// <param name="message">具体报警信息</param>
        /// <param name="sourceType">报警分类</param>
        /// <param name="sourceName">报警来自的部件名称</param>
        /// <param name="val">报警值</param>
        public void Insert(string message, AlarmCategory sourceType, string sourceName, double val=0)
        {
            Insert(message, sourceType, sourceName, new OperandDef(val));
        }
        
        /// <summary>
        /// 插入一条报警信息
        /// </summary>
        /// <param name="message">具体报警信息</param>
        /// <param name="sourceType">报警分类</param>
        /// <param name="sourceName">报警来自的部件名称</param>
        /// <param name="val">报警值</param>
        public void Insert(string message, AlarmCategory sourceType, string sourceName, OperandDef val)
        {
            // 如果线程未运行，不能插入报警信息
            if (!Running)
                return;
            
            // 确认非空
            _items ??= [];
    
            // 检查是否存在同类报警
            foreach (var item in _items.Values.Where(item => item.SourceType == sourceType && item.SourceName == sourceName))
            {
                item.Val = val;
                item.HappenDt = DateTime.Now;
                item.Counter += 1;
                return;
            }
            
            // 列表中没有出现同类报警
            var alarmItem = new AlarmItem
            {
                Description = message,
                SourceType = sourceType,
                SourceName = sourceName,
                Val = val
            };
            _items?.TryAdd(alarmItem.Identify, alarmItem);
        }
        
        /// <summary>
        /// 获取报警信息
        /// </summary>
        /// <returns></returns>
        public Dictionary<Guid,string> AcquireAlarm()
        {
            Dictionary<Guid, string> lstMessage = [];
            if (_items == null) return lstMessage;
            foreach (var item in _items.Values.OrderBy(a => a.CreateDt).Where(a => !a.Hidden))
            {
                lstMessage.TryAdd(item.Identify, item.ToString());
            }
            return lstMessage;
        }

        /// <summary>
        /// 确认报警
        /// </summary>
        /// <param name="id"></param>
        public void ConfirmAlarm(Guid id)
        {
            if (_items == null) return;
            if (Guid.Empty == id)
            {
                foreach (var item in _items.Values)
                {
                    item.ConfirmDt = DateTime.Now;
                    item.Confirm = true;
                    
                    _confirmItems?.TryAdd(item.Identify, item);
                }

                _items.Clear();
                return;
            }

            if (!_items.TryGetValue(id, out var item1)) return;
            item1.ConfirmDt = DateTime.Now;
            item1.Confirm = true;
            _items.TryRemove(id, out var rm);
            _confirmItems?.TryAdd(item1.Identify, item1);
        }

        /// <summary>
        /// 隐藏报警
        /// </summary>
        /// <param name="id"></param>
        public void HiddenAlarm(Guid id)
        {
            if (_items == null) return;
            if (Guid.Empty == id)
            {
                foreach (var item in _items.Values)
                {
                    item.Hidden = true;
                }
                return;
            }

            if (_items.TryGetValue(id, out var item1))
            {
                item1.Hidden = true;
            }
        }

        /// <summary>
        /// 显示报警
        /// </summary>
        public void ShowAllAlarm()
        {
            if (_items == null) return;
            foreach (var item in _items.Values)
            {
                item.Hidden = false;
            }
        }

        /// <summary>
        /// 缓冲器报警记录长度
        /// </summary>
        public int TotalAlarmNum => _items?.Count ?? 0;

        /// <summary>
        /// 工作者线程
        /// </summary>
        private void WorkerThread()
        {
            var lastTicks = Environment.TickCount;
            int savedTimerout = 0;

            while (Running)
            {
                var commTicks = Environment.TickCount;
                //----------------------------------------------------------------------
                // 确认过报警队列非空， 队列数据超过100条，或者未保持数据的时间超过2秒
                if (_confirmItems is not null && (_confirmItems.Count > 100 || savedTimerout > 20))
                {
                    SaveToTable(false, true);
                    _confirmItems.Clear();
                    savedTimerout = 0;
                }

                savedTimerout += 1;
                
                // ------------时间计算---------------------------------------------------
                var temp = Environment.TickCount;
                Cycle = temp - lastTicks;
                Lapse = temp - commTicks; 
                lastTicks = temp;
                var deltaTicks = Interval - Lapse;
                if (deltaTicks > 0)
                {
                    Thread.Sleep(deltaTicks);
                }
            }
        }

        /// <summary>
        /// 保存报警数据到报警数据中
        /// </summary>
        /// <returns></returns>
        private bool SaveToTable(bool bNotConfirm, bool bConfirm)
        {
            try
            {
                if (_serviceScopeFactory != null)
                {
                    using var scope = _serviceScopeFactory.CreateScope();
                    var wrapper = scope.ServiceProvider.GetRequiredService<IRepositoryWrapper>();

                    // 保存未确认队列中的所有报警信息
                    if (_items?.Values != null && bNotConfirm)
                        foreach (var item in _items.Values)
                        {
                            var entity = item.CreateAlarmTable();
                            wrapper.Alarm.Add(entity);
                        }

                    // 保存已经确认队列中的所有报警信息
                    if (_confirmItems?.Values != null && bConfirm)
                        foreach (var item in _confirmItems.Values)
                        {
                            var entity = item.CreateAlarmTable();
                            wrapper.Alarm.Add(entity);
                        }

                    return true;
                }
            }
            catch (Exception ex)
            {
                Insert($"保存报警变发生错误, {ex.Message}", AlarmCategory.ALARM, "Alarm");
            }

            return false;
        }

    }
}
